package wallet

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/zeromicro/go-zero/core/stores/cache"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
	"navi-mumbai/common/gensql"
	"navi-mumbai/common/key/walletkey"
	"time"
)

var _ UserWithdrawFlowModel = (*customUserWithdrawFlowModel)(nil)

type (
	// UserWithdrawFlowModel is an interface to be customized, add more methods here,
	// and implement the added methods in customUserWithdrawFlowModel.
	UserWithdrawFlowModel interface {
		userWithdrawFlowModel
		InsertTrans(ctx context.Context, tx sqlx.Session, data *UserWithdrawFlow) (sql.Result, error)
		UpdateStateTrans(ctx context.Context, tx sqlx.Session, flowNo int64, state int64, gwFlowNo int64) error
		UpdateAuditTrans(ctx context.Context, tx sqlx.Session, flowNo int64, state int64, auditMsg string, auditAdminUid int64) error
		UpdateAudit(ctx context.Context, flowNo int64, state int64, auditMsg string, auditAdminUid int64) error
		UpdatePush(ctx context.Context, flowNo int64, state int64, pushMsg string) error
		UpdateState(ctx context.Context, flowNo int64, state int64, exFlow string, errMsg string, gwFlowNo int64) error
		Find(ctx context.Context, uid int64, moneyType int64, currencyType int64, payChannelId int64, state []int64, pageNo, pageSize int64, start, end *time.Time) ([]*UserWithdrawFlow, error)
		Count(ctx context.Context, uid int64, moneyType int64, currencyType int64, payChannelId int64, state []int64, start, end *time.Time) (int64, error)

		SumAmount(ctx context.Context, payChannelId int64, start, end *time.Time) (int64, error)
		CountSuccessUser(ctx context.Context, payChannelId int64, start, end *time.Time) (int64, error)
		CountSuccessUser2(ctx context.Context, start, end *time.Time) (int64, error)
		CountSuccessFlow(ctx context.Context, payChannelId int64, start, end *time.Time) (int64, error)

		SumNewUserAmount(ctx context.Context, startUid, endUid int64, start, end *time.Time) (int64, error)
		CountSuccessNewUser(ctx context.Context, startUid, endUid int64, start, end *time.Time) (int64, error)
	}

	customUserWithdrawFlowModel struct {
		*defaultUserWithdrawFlowModel
		genSql *gensql.GenSql
	}
)

// NewUserWithdrawFlowModel returns a model for the database table.
func NewUserWithdrawFlowModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) UserWithdrawFlowModel {
	return &customUserWithdrawFlowModel{
		defaultUserWithdrawFlowModel: newUserWithdrawFlowModel(conn, c, opts...),
		genSql:                       gensql.NewGenSql(),
	}
}

func (m *customUserWithdrawFlowModel) InsertTrans(ctx context.Context, tx sqlx.Session, data *UserWithdrawFlow) (sql.Result, error) {
	query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, userWithdrawFlowRowsExpectAutoSet)
	ret, err := tx.ExecCtx(ctx, query, data.FlowNo, data.Uid, data.MoneyType, data.MoneyAmount, data.CurrencyType, data.CurrencyAmount, data.PayChannelId, data.AuditAdminUid, data.AuditMsg, data.AuditTime,
		data.State, data.PushMsg, data.PushTime, data.ExternalFlowNo, data.AccountName, data.ErrMsg, data.AccountType, data.GwFlowNo, data.BankCardNumber, data.BankCode, data.BankName, data.ServiceFeeRate, data.ServiceFeeAmount)
	return ret, err
}

func (m *customUserWithdrawFlowModel) UpdateStateTrans(ctx context.Context, tx sqlx.Session, flowNo int64, state int64, gwFlowNo int64) error {
	query := fmt.Sprintf("update %s set state = ?, gw_flow_no = ? where flow_no = ?", m.table)
	_, err := tx.ExecCtx(ctx, query, state, gwFlowNo, flowNo)
	if err != nil {
		return err
	}
	return nil
}

func (m *customUserWithdrawFlowModel) UpdateAuditTrans(ctx context.Context, tx sqlx.Session, flowNo int64, state int64, auditMsg string, auditAdminUid int64) error {
	query := fmt.Sprintf("update %s set state = ?, audit_admin_uid = ?, audit_msg = ?, audit_time = ? where flow_no = ?", m.table)
	rs, err := tx.ExecCtx(ctx, query, state, auditAdminUid, auditMsg, time.Now(), flowNo)

	if err != nil {
		return err
	}
	ra, err := rs.RowsAffected()
	if err != nil {
		return err
	}
	if ra == 0 {
		return ErrNotFound
	}
	return nil
}

func (m *customUserWithdrawFlowModel) UpdateAudit(ctx context.Context, flowNo int64, state int64, auditMsg string, auditAdminUid int64) error {
	query := fmt.Sprintf("update %s set state = ?, audit_admin_uid = ?, audit_msg = ?, audit_time = ? where flow_no = ?", m.table)
	rs, err := m.ExecNoCacheCtx(ctx, query, state, auditAdminUid, auditMsg, time.Now(), flowNo)

	if err != nil {
		return err
	}
	ra, err := rs.RowsAffected()
	if err != nil {
		return err
	}
	if ra == 0 {
		return ErrNotFound
	}
	return nil
}

func (m *customUserWithdrawFlowModel) UpdatePush(ctx context.Context, flowNo int64, state int64, pushMsg string) error {
	query := fmt.Sprintf("update %s set state = ?, push_msg = ?, push_time = ? where flow_no = ?", m.table)
	rs, err := m.ExecNoCacheCtx(ctx, query, state, pushMsg, time.Now(), flowNo)

	if err != nil {
		return err
	}
	ra, err := rs.RowsAffected()
	if err != nil {
		return err
	}
	if ra == 0 {
		return ErrNotFound
	}
	return nil
}

func (m *customUserWithdrawFlowModel) UpdateState(ctx context.Context, flowNo int64, state int64, exFlow string, errMsg string, gwFlowNo int64) error {
	query := fmt.Sprintf("update %s set state = ?, external_flow_no = ?, err_msg = ?, gw_flow_no = ? where flow_no = ?", m.table)
	_, err := m.ExecNoCacheCtx(ctx, query, state, exFlow, errMsg, gwFlowNo, flowNo)
	if err != nil {
		return err
	}
	return nil
}

func (m *customUserWithdrawFlowModel) Find(ctx context.Context, uid int64, moneyType int64, currencyType int64, payChannelId int64, state []int64, pageNo, pageSize int64, start, end *time.Time) ([]*UserWithdrawFlow, error) {
	// 分页
	if pageNo < 1 {
		pageNo = 1
	}
	if pageSize <= 0 {
		pageSize = 10
	}

	query := fmt.Sprintf("select %s from %s ", userWithdrawFlowRows, m.table)
	m.genSql.InitSql(query)

	if start != nil {
		m.genSql = m.genSql.Where("create_time >= ?", start)
	}
	if end != nil {
		m.genSql = m.genSql.Where("create_time <= ?", end)
	}
	if uid != 0 {
		m.genSql = m.genSql.Where("uid = ?", uid)
	}
	if moneyType != 0 {
		m.genSql = m.genSql.Where("money_type = ?", moneyType)
	}
	if currencyType != 0 {
		m.genSql = m.genSql.Where("currency_type = ?", currencyType)
	}
	if payChannelId != 0 {
		m.genSql = m.genSql.Where("pay_channel_id = ?", payChannelId)
	}
	if len(state) != 0 {
		if len(state) > 1 {
			m.genSql = m.genSql.Where("state in (?)", state)
		} else {
			m.genSql = m.genSql.Where("state = ?", state[0])
		}
	}
	q, v, err := m.genSql.OrderBy("create_time desc").Limit(uint64(pageSize)).Offset(uint64((pageNo - 1) * pageSize)).ToSql()
	if err != nil {
		return nil, err
	}

	list := make([]*UserWithdrawFlow, 0)
	err = m.QueryRowsNoCacheCtx(ctx, &list, q, v...)
	switch err {
	case nil:
		return list, nil
	default:
		return nil, err
	}
}

func (m *customUserWithdrawFlowModel) Count(ctx context.Context, uid int64, moneyType int64, currencyType int64, payChannelId int64, state []int64, start, end *time.Time) (int64, error) {

	query := fmt.Sprintf("select count(flow_no) from %s ", m.table)
	m.genSql.InitSql(query)

	if start != nil {
		m.genSql = m.genSql.Where("create_time >= ?", start)
	}
	if end != nil {
		m.genSql = m.genSql.Where("create_time <= ?", end)
	}
	if uid != 0 {
		m.genSql = m.genSql.Where("uid = ?", uid)
	}
	if moneyType != 0 {
		m.genSql = m.genSql.Where("money_type = ?", moneyType)
	}
	if currencyType != 0 {
		m.genSql = m.genSql.Where("currency_type = ?", currencyType)
	}
	if payChannelId != 0 {
		m.genSql = m.genSql.Where("pay_channel_id = ?", payChannelId)
	}
	if len(state) != 0 {
		if len(state) > 1 {
			m.genSql = m.genSql.Where("state in (?)", state)
		} else {
			m.genSql = m.genSql.Where("state = ?", state[0])
		}
	}
	q, v, err := m.genSql.ToSql()
	if err != nil {
		return 0, err
	}

	var resp int64
	err = m.QueryRowNoCacheCtx(ctx, &resp, q, v...)
	switch err {
	case nil:
		return resp, nil
	default:
		return 0, err
	}
}

func (m *customUserWithdrawFlowModel) SumAmount(ctx context.Context, payChannelId int64, start, end *time.Time) (int64, error) {

	q := fmt.Sprintf("select coalesce(sum(money_amount), 0) from %s ", m.table)
	m.genSql.InitSql(q)

	if start != nil {
		m.genSql = m.genSql.Where("update_time >= ?", start)
	}
	if end != nil {
		m.genSql = m.genSql.Where("update_time <= ?", end)
	}
	if payChannelId != 0 {
		m.genSql = m.genSql.Where("pay_channel_id = ?", payChannelId)
	}
	m.genSql = m.genSql.Where("state in (?, ?)", walletkey.WithdrawFlowStateSuccess, walletkey.WithdrawFlowPushed)
	query, args, err := m.genSql.ToSql()
	if err != nil {
		return 0, err
	}
	var resp int64
	err = m.QueryRowNoCacheCtx(ctx, &resp, query, args...)
	switch err {
	case nil:
		return resp, nil
	default:
		return 0, err
	}
}

const countWithdrawUserSql = "select count(uid) from (select uid from %s where update_time between ? and ? and state in (? , ?) and pay_channel_id = ? group by uid having count(uid) >= 1) t"

func (m *customUserWithdrawFlowModel) CountSuccessUser(ctx context.Context, payChannelId int64, start, end *time.Time) (int64, error) {
	var cnt int64
	var err error
	query := fmt.Sprintf(countWithdrawUserSql, m.table)
	err = m.QueryRowNoCacheCtx(ctx, &cnt, query, start, end, walletkey.WithdrawFlowStateSuccess, walletkey.WithdrawFlowPushed, payChannelId)
	switch err {
	case nil:
		return cnt, nil
	default:
		return 0, err
	}
}

const countWithdrawUserSql2 = "select count(uid) from (select uid from %s where update_time between ? and ? and state in (? , ?) group by uid having count(uid) >= 1) t"

func (m *customUserWithdrawFlowModel) CountSuccessUser2(ctx context.Context, start, end *time.Time) (int64, error) {
	var cnt int64
	var err error
	query := fmt.Sprintf(countWithdrawUserSql2, m.table)
	err = m.QueryRowNoCacheCtx(ctx, &cnt, query, start, end, walletkey.WithdrawFlowStateSuccess, walletkey.WithdrawFlowPushed)
	switch err {
	case nil:
		return cnt, nil
	default:
		return 0, err
	}
}

func (m *customUserWithdrawFlowModel) CountSuccessFlow(ctx context.Context, payChannelId int64, start, end *time.Time) (int64, error) {
	q := fmt.Sprintf("select count(flow_no) from %s ", m.table)
	m.genSql.InitSql(q)

	if start != nil {
		m.genSql = m.genSql.Where("update_time >= ?", start)
	}
	if end != nil {
		m.genSql = m.genSql.Where("update_time <= ?", end)
	}
	if payChannelId != 0 {
		m.genSql = m.genSql.Where("pay_channel_id = ?", payChannelId)
	}
	m.genSql = m.genSql.Where("state in (?, ?)", walletkey.WithdrawFlowStateSuccess, walletkey.WithdrawFlowPushed)

	q, v, err := m.genSql.ToSql()
	if err != nil {
		return 0, err
	}

	var resp int64
	err = m.QueryRowNoCacheCtx(ctx, &resp, q, v...)
	switch err {
	case nil:
		return resp, nil
	default:
		return 0, err
	}
}

func (m *customUserWithdrawFlowModel) SumNewUserAmount(ctx context.Context, startUid, endUid int64, start, end *time.Time) (int64, error) {
	q := fmt.Sprintf("select coalesce(sum(money_amount), 0) from %s ", m.table)
	m.genSql.InitSql(q)
	if start != nil {
		m.genSql = m.genSql.Where("update_time >= ?", start)
	}
	if end != nil {
		m.genSql = m.genSql.Where("update_time <= ?", end)
	}
	if startUid != 0 {
		m.genSql = m.genSql.Where("uid >= ?", startUid)
	}
	if endUid != 0 {
		m.genSql = m.genSql.Where("uid <= ?", endUid)
	}
	m.genSql = m.genSql.Where("state = ?", walletkey.WithdrawFlowStateSuccess)
	query, args, err := m.genSql.ToSql()
	if err != nil {
		return 0, err
	}
	var resp int64
	err = m.QueryRowNoCacheCtx(ctx, &resp, query, args...)
	switch err {
	case nil:
		return resp, nil
	default:
		return 0, err
	}
}

const countWithdrawUserSql3 = "select count(uid) from (select uid from %s where update_time between ? and ? and state = ? and uid between ? and ? group by uid having count(uid) >= 1) t"

func (m *customUserWithdrawFlowModel) CountSuccessNewUser(ctx context.Context, startUid, endUid int64, start, end *time.Time) (int64, error) {
	var cnt int64
	var err error
	query := fmt.Sprintf(countWithdrawUserSql3, m.table)
	err = m.QueryRowNoCacheCtx(ctx, &cnt, query, start, end, walletkey.WithdrawFlowStateSuccess, startUid, endUid)
	switch err {
	case nil:
		return cnt, nil
	default:
		return 0, err
	}
}
